#ifndef LINUX_DISK_H
#define LINUX_DISK_H

#include <types.h>

#define SECTOR_SIZE     512

#define DISK_NAME_SIZE  32
#define PART_NAME_SIZE  32
#define DISK_PART_SIZE  (4 + 1)
#define DISK_SIZE       2

#define IOBASE_PRIMARY      0x1F0
#define DISK_DATA(disk)     (disk->port_base + 0)
#define DISK_ERROR(disk)    (disk->port_base + 1)
#define DISK_SECTOR(disk)   (disk->port_base + 2)
#define DISK_LBA_L(disk)    (disk->port_base + 3)
#define DISK_LBA_M(disk)    (disk->port_base + 4)
#define DISK_LBA_H(disk)    (disk->port_base + 5)
#define DISK_DRIVE(disk)    (disk->port_base + 6)
#define DISK_STATUS(disk)   (disk->port_base + 7)
#define DISK_CMD(disk)      (disk->port_base + 7)

#define DISK_CMD_IDENTIFY   0xEC
#define DISK_CMD_READ       0x24
#define DISK_CMD_WRITE      0x34
#define DISK_STATUS_ERR     (1 << 0)
#define DISK_STATUS_DRQ     (1 << 3)
#define DISK_STATUS_DF      (1 << 5)
#define DISK_STATUS_BUSY    (1 << 7)

#define DISK_DRIVE_BASE     0xE0

typedef struct disk_s disk_t;
typedef struct disk_part_info_s disk_part_info_t;

// partinfo_t
typedef struct disk_part_t {
    char name[PART_NAME_SIZE];
    disk_t *disk;
    enum {
        FS_INVALID = 0x00, FS_FAT16_0 = 0x06, FS_FAT16_1 = 0x0E
    } type;
    int start_sector;
    int total_sector;
} disk_part_t;

struct disk_s {
    char name[DISK_NAME_SIZE];
    enum {
        DISK_MASTER = (0 << 4),
        DISK_SLAVE = (1 << 4)
    } type;
    int port_base;
    int sector_size;
    int sector_count;
    disk_part_t parts[DISK_PART_SIZE];
} _packed;

// part_item_t
struct disk_part_info_s {
    uint8_t boot_active;
    uint8_t start_header;
    uint16_t start_sector:6;
    uint16_t start_cylinder:10;
    uint8_t type;
    uint8_t end_header;
    uint16_t end_sector:6;
    uint16_t end_cylinder:10;
    uint32_t sector;
    uint32_t total_sectors;
} _packed;

// mbr_t
typedef struct disk_mbr_s disk_mbr_t;
typedef struct disk_mbr_s {
    uint8_t code[446];
    disk_part_info_t parts[4];
    uint8_t boot_sign[2];
} _packed;

void init_disk();

void read_part_info(disk_t *disk);

int identify_disk(disk_t *disk);

void print_disk_info(disk_t *disk);

#endif